#include "BasicDispatcher.h"
#include "CastingPolicy.h"

/**
 * A wrapper for BasicDispatcher, automated conversions
 * pass function pointer as the member function template's template parameter
 */
template <class BaseLhs, class BaseRhs = BaseLhs, typename ResultType = void,
		  template<class,class> class CastingPolicy = DynamicCaster,
		  template<class,class,class,class> class DispatcherBackend = BasicDispatcher>
class FnDispatcher
{
	DispatcherBackend<BaseLhs,BaseRhs,ResultType,ResultType (*)(BaseLhs&, BaseRhs&)> backEnd_;
public:
	template<class ConcreteLhs,class ConcreteRhs, ResultType (*callback)(ConcreteLhs&,ConcreteRhs&),
			 bool symmetric>
	void Add();
	ResultType Go(BaseLhs& lhs, BaseRhs& rhs){
		return backEnd_.Go(lhs,rhs);
	}
};

template <class BaseLhs, class BaseRhs, typename ResultType,
	      template<class,class> class CastingPolicy,
		  template<class,class,class,class> class DispatcherBackend
>
template<class ConcreteLhs,class ConcreteRhs, ResultType (*callback)(ConcreteLhs&,ConcreteRhs&),
		 bool symmetric>
void FnDispatcher<BaseLhs,BaseRhs,ResultType,CastingPolicy,DispatcherBackend>::Add()
{
	struct Local
	{
		static ResultType Trampoline(BaseLhs& lhs,BaseRhs& rhs) {
			/*return callback(dynamic_cast<ConcreteLhs&>(lhs),dynamic_cast<ConcreteRhs&>(rhs));*/
			return callback(CastingPolicy<ConcreteLhs,BaseLhs>::Cast(lhs),
							CastingPolicy<ConcreteRhs,BaseRhs>::Cast(rhs));

		}
		static ResultType TrampolineR(BaseRhs& rhs, BaseLhs& lhs) {
			return Trampoline(lhs,rhs);
		}
	};
	backEnd_.Add<ConcreteLhs, ConcreteRhs>(&Local::Trampoline); //isn't template keyword needed?
	if(symmetric)
		backEnd_.Add<ConcreteRhs,ConcreteLhs>(&Local::TrampolineR);
}